package main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
)

// type ffmp4info struct {
// 	Streams []struct {
// 		CodecName string `json:"codec_name"`
// 		Width     int    `json:"width"`
// 		Height    int    `json:"height"`
// 	} `json:"streams"`
// }

type Mp4Info struct {
	Width    int
	Height   int
	HasAudio bool
	x        int
	y        int
}

func parseMp4(filename string) Mp4Info {
	// cmdArgs := []string{"-show_entries", "format=duration,size,bit_rate,filename", "-show_streams", "-v", "quiet", "-of", "csv=\"p=0\"", "-of", "json", "-i", filename}
	// cmd := exec.Command("ffprobe", cmdArgs...)
	// var out bytes.Buffer
	// cmd.Stdout = &out
	// if err := cmd.Run(); err != nil {
	// 	return nil, err
	// }
	// // fmt.Printf("%s\n", out.String())
	// var info ffmp4info
	// if err := json.Unmarshal(out.Bytes(), &info); err != nil {
	// 	return nil, err
	// }
	// return &info, nil
	var info Mp4Info
	cmd := exec.Command("ffmpeg", "-i", filename)
	out, _ := cmd.CombinedOutput()
	// fmt.Printf("%s %s\n", filename, out)
	pos := bytes.Index(out, []byte("Stream #0:"))
	arr := bytes.Split(out[pos:], []byte(","))
	for _, v := range arr {
		if bytes.Contains(v, []byte(")")) || !bytes.Contains(v, []byte("x")) {
			continue
		}
		fmt.Sscanf(string(v), "%dx%d", &info.Width, &info.Height)
		break
	}
	if bytes.Count(out, []byte("Stream #0:")) > 1 {
		info.HasAudio = true
	}
	return info
}

func GetInputInfo(inputs []string) (files []Mp4Info, cmdArgs []string, maxW, maxH int) {
	for _, f := range inputs {
		info := parseMp4(f)
		if info.Width <= 0 || info.Height <= 0 {
			return
		}
		maxW = max(info.Width, maxW)
		maxH = max(info.Height, maxH)
		files = append(files, info)
		cmdArgs = append(cmdArgs, "-i")
		cmdArgs = append(cmdArgs, f)
	}
	return
}

type DrawRect struct {
	Width  int
	Height int
	Num    int
}

func (d DrawRect) GetPos(i, n int, m *Mp4Info) {
	switch {
	case d.Num > 6 || d.Num < 5:
		col, row := i/n, i%n
		m.x = row*d.Width + (d.Width-m.Width)/2
		m.y = col*d.Height + (d.Height-m.Height)/2
	default:
		switch i {
		case 0:
			m.x = d.Width - m.Width
			m.y = d.Height - m.Height
		case 1:
			m.x = 2*d.Width + (d.Width-m.Width)/2
			m.y = (d.Height - m.Height) / 2
		case 2:
			m.x = 2*d.Width + (d.Width-m.Width)/2
			m.y = d.Height + (d.Height-m.Height)/2
		case 3:
			m.x = (d.Width - m.Width) / 2
			m.y = 2*d.Height + (d.Height-m.Height)/2
		case 4:
			m.x = d.Width + (d.Width-m.Width)/2
			m.y = 2*d.Height + (d.Height-m.Height)/2
		case 5:
			m.x = 2*d.Width + (d.Width-m.Width)/2
			m.y = 2*d.Height + (d.Height-m.Height)/2
		}
	}
}

func (d DrawRect) Complexs(cs []Mp4Info) (string, int) {
	str, astr, aMixChn, n := "", "", 0, 0
	switch {
	case d.Num < 3:
		n = 2
		str = fmt.Sprintf("pad=%d:%d:", n*d.Width, d.Height)
	case d.Num < 5:
		n = 2
	case d.Num < 7:
		n = 3
		str = fmt.Sprintf("scale=%d:%d,pad=%d:%d:", 2*d.Width, 2*d.Height, n*d.Width, n*d.Height)
	case d.Num < 10:
		n = 3
	case d.Num < 17:
		n = 4
	case d.Num < 26:
		n = 5
	}
	if str == "" {
		str = fmt.Sprintf("pad=%d:%d:", n*d.Width, n*d.Height)
	}
	for i, v := range cs {
		d.GetPos(i, n, &v)
		if i == 0 {
			str += fmt.Sprintf("%d:%d:black", v.x, v.y)
		} else {
			cr := i + 'a' - 1
			str += fmt.Sprintf("[%c];[%c][%d:v]overlay=%d:%d", cr, cr, i, v.x, v.y)
		}
		if v.HasAudio {
			astr += fmt.Sprintf("[%d:a]", i)
			aMixChn++
		}
	}
	str += "[vout]"
	if aMixChn > 0 {
		str += fmt.Sprintf(";%samix=inputs=%d:duration=first[aout]", astr, aMixChn)
	}
	return str, aMixChn
}

func Complex() {
	flag.Parse()
	inputs := os.Args[1 : len(os.Args)-1]
	files, cmdArgs, w, h := GetInputInfo(inputs)
	if len(files) < 4 {
		return
	}
	draw := DrawRect{Width: w, Height: h, Num: len(files)}
	cmdArgs = append(cmdArgs, "-threads")
	cmdArgs = append(cmdArgs, "12")
	cmdArgs = append(cmdArgs, "-preset")
	cmdArgs = append(cmdArgs, "ultrafast")
	cmdArgs = append(cmdArgs, "-filter_complex")
	cmdStr, aChlN := draw.Complexs(files)
	cmdArgs = append(cmdArgs, cmdStr)
	cmdArgs = append(cmdArgs, "-map")
	cmdArgs = append(cmdArgs, "[vout]")
	if aChlN > 0 {
		cmdArgs = append(cmdArgs, "-map")
		cmdArgs = append(cmdArgs, "[aout]")
	}
	cmdArgs = append(cmdArgs, os.Args[len(os.Args)-1])
	cmdArgs = append(cmdArgs, "-y")
	fmt.Println(cmdArgs)
	exec.Command("ffmpeg", cmdArgs...).Run()
}
